﻿using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Drawing;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using System.Windows.Shapes;
using Labthink.PCSoft.UniversalHost4.Model;
using Labthink.PCSoft.UniversalHost4.Utility;
using NLog.Fluent;
using Brushes = System.Windows.Media.Brushes;
using FullData = Labthink.PCSoft.UniversalHost4.Model.FullData;
using Point = System.Windows.Point;

namespace Chart
{
    public partial class UcCurve
    {
        private const int GridCount = 10;
        //private const int MaxYThreshold = 5;
        private double _xScal;
        private double _yScal;
        private readonly Path _axisPath;
        private readonly Path _bgPath;
        private bool _isDrawBg;//确保只画一次背景
        private List<Line> _lines;
        private Axis _x;
        private int _xId;
        private LineStyleManager _style;
        private ObservableCollection<TextAndValue> _curves;

        public UcCurve()
        {
            InitializeComponent();
            _curves = new ObservableCollection<TextAndValue>();
            TxtCurves.SelectionChanged += TxtCurves_SelectionChanged;
            _axisPath = new Path
            {
                Stroke = Brushes.Black,
                StrokeThickness = 1
            };

            _bgPath = new Path
            {
                Stroke = Brushes.LightGray,
                StrokeThickness = 1,
                StrokeDashArray = new DoubleCollection { 5, 1 }
            };

            _lines = new List<Line>();
            _style = new LineStyleManager();
            CurveCanvas.Children.Add(_axisPath);
            CurveCanvas.Children.Add(_bgPath);
            //_flash();
        }

        private void TxtCurves_SelectionChanged(object sender, SelectionChangedEventArgs e)
        {
            if (TxtCurves.SelectedItem is TextAndValue tv)
            {
                foreach (var line in _lines)
                {
                    if (line.Id == tv.Value.ToInt())
                    {
                        line.Show();
                    }
                    else
                    {
                        line.Hide();
                    }
                }
            }
        }

        private void UcCurve_OnLoaded(object sender, RoutedEventArgs e)
        {
            TxtCurves.Init(_curves, "Text", "Value");
        }

        private void UcCurve_OnSizeChanged(object sender, SizeChangedEventArgs e)
        {
            //_axisPath.Data = null;
            //暂不实现自动缩放功能，调用者请使用Viewbox
        }

        /// <summary>
        /// 绘制背景网格和坐标轴
        /// </summary>
        private void _drawBgAndAxis()
        {
            var axis = new PathGeometry();
            //X轴
            var xpf = new PathFigure
            {
                StartPoint = new Point(0, CurveCanvas.Height)
            };

            xpf.Segments.Add(new LineSegment { Point = new Point(CurveCanvas.Width, CurveCanvas.Height) });
            axis.Figures.Add(xpf);
            xpf = new PathFigure
            {
                StartPoint = new Point(0, 0)
            };

            xpf.Segments.Add(new LineSegment { Point = new Point(CurveCanvas.Width, 0) });
            axis.Figures.Add(xpf);

            //Y轴
            var ypf = new PathFigure
            {
                StartPoint = new Point(0, 0)
            };
            ypf.Segments.Add(new LineSegment { Point = new Point(0, CurveCanvas.Height) });
            axis.Figures.Add(ypf);
            ypf = new PathFigure
            {
                StartPoint = new Point(CurveCanvas.Width, 0)
            };
            ypf.Segments.Add(new LineSegment { Point = new Point(CurveCanvas.Width, CurveCanvas.Height) });
            axis.Figures.Add(ypf);

            //添加刻度
            _xScal = CurveCanvas.Width / GridCount;
            _yScal = CurveCanvas.Height / GridCount;
            //var xbc = (int)(_xScal / BaseGridRuler);
            //var ybc = (int)(_yScal / BaseGridRuler);
            //_xScal = xbc * BaseGridRuler;
            //_yScal = ybc * BaseGridRuler;
            var bg = new PathGeometry();
            for (int i = 1; i < GridCount; i++)
            {
                var pf = new PathFigure { StartPoint = new Point(0, _yScal * i) };
                pf.Segments.Add(new LineSegment { Point = new Point(CurveCanvas.Width, _yScal * i) });
                bg.Figures.Add(pf);
                pf = new PathFigure { StartPoint = new Point(_xScal * i, 0) };
                pf.Segments.Add(new LineSegment { Point = new Point(_xScal * i, CurveCanvas.Height) });
                bg.Figures.Add(pf);

                //pf = new PathFigure { StartPoint = new Point(0, _yScal * i) };
                //pf.Segments.Add(new LineSegment { Point = new Point(5, _yScal * i) });
                //axis.Figures.Add(pf);

                //pf = new PathFigure { StartPoint = new Point(_xScal * i, CurveCanvas.ActualHeight) };
                //pf.Segments.Add(new LineSegment { Point = new Point(_xScal * i, CurveCanvas.ActualHeight - 5) });
                //axis.Figures.Add(pf);
            }
            _axisPath.Data = axis;
            _bgPath.Data = bg;

            // _calcScaleOfYHeight();

        }

        /// <summary>
        /// 计算Y轴刻度所占高度
        /// </summary>
        //private void _calcScaleOfYHeight()
        //{
        //    GridYScale.RowDefinitions[0].Height = new GridLength(60 - _yScal / 2);
        //    GridYScale.RowDefinitions[GridYScale.RowDefinitions.Count - 1].Height = new GridLength(50 - _yScal / 2);
        //}

        private void _initYAxis()
        {
            var yls = _lines.Where(l => l.IsMonopolizeY).ToList();

            //var showCount = yls.Count > 2 ? 6 : 11;
            var showCount = 11;
            //var last = yls.Count > 2 ? _yScal : _yScal / 2;
            var last = _yScal;
            var lastTop = new GridLength(60);
            var lastBottom = new GridLength(30);
            GridYScale.RowDefinitions.Clear();
            GridYScale.RowDefinitions.Add(new RowDefinition { Height = lastTop });
            for (int i = 0; i < showCount; i++)
            {
                GridYScale.RowDefinitions.Add(new RowDefinition());
            }
            GridYScale.RowDefinitions.Add(new RowDefinition { Height = lastBottom });
            for (int i = 1; i <= showCount; i++)
            {
                var sp = new StackPanel { Orientation = Orientation.Vertical, VerticalAlignment = VerticalAlignment.Center };
                Grid.SetRow(sp, i);
                GridYScale.Children.Add(sp);
                for (int j = 0; j < _lines.Count; j++)
                {
                    var y = _lines[j].Y;
                    var tl = (y.Max - (i - 1) * (y.Max - y.Min) / (showCount - 1));//.Round();
                    var t = tl.Round(y.Precision);
                    if (y.Type == 2)
                    {
                        //科学计数
                        t = tl.ScientificNotation(y.Precision);
                    }

                    var tb = new TextBlock
                    {
                        Text = t,
                        FontSize = 12,
                        Foreground = _lines[j].LinePath.Stroke,
                        HorizontalAlignment = HorizontalAlignment.Right,
                        VerticalAlignment = VerticalAlignment.Center,
                        Margin = new Thickness(0, 0, 2, 0),
                        Tag = true,
                        Visibility = _lines[j].IsShow ? Visibility.Visible : Visibility.Collapsed
                    };
                    _lines[j].YScales.Add(tb);
                    sp.Children.Add(tb);
                }
            }
        }

        private void _initBtnAndLegend()
        {
            foreach (var line in _lines)
            {
                //SpButtons.Children.Add(line.LineLabel);
                WpLegends.Children.Add(line.Legend);
            }
        }

        /// <summary>
        ///  添加一条Y轴曲线
        /// </summary>
        /// <param name="data">数据描述</param>
        /// <param name="isMonopolizeY">是否独立Y轴</param>
        /// <param name="showPoint">是否显示数据点</param>
        /// <param name="max">最大值</param>
        /// <param name="min">最小值</param>
        /// <param name="index">并列试验编号，默认为空</param>
        public void AddYAxis(FullData data, bool isMonopolizeY, bool showPoint, double? max = null, double? min = null, int? index = null)
        {
            Dispatcher.Invoke(() =>
            {
                var l = new Line(_style, showPoint, data.Text, data.Unit == "null" ? "" : data.Unit, data.Precision, index)
                {
                    Id = data.DataNo,
                    IsMonopolizeY = isMonopolizeY,
                    IsShow = true
                };

                l.OnPointAddHandler += L_OnPointAddHandler;
                if (max.HasValue)
                {
                    l.Y.Max = max.Value;
                    l.Y.OriginMax = max.Value;
                }

                if (min.HasValue)
                {
                    l.Y.Min = min.Value;
                    l.Y.OriginMin = min.Value;
                }

                if (data.DataType == DataType.Int)
                {
                    l.Y.Type = 1;
                }

                else if (data.DataType == DataType.EDouble)
                {
                    l.Y.Type = 2;
                }

                l.Hide();

                l.MinX = _x.Min;
                l.MaxX = _x.Max;
                l.ParentH = CurveCanvas.Height;
                l.ParentW = CurveCanvas.Width;
                _lines.Add(l);
                _curves.Add(new TextAndValue { Text = data.Text, Value = l.Id + "" });
                if (_lines.Count == 1)
                {
                    TxtCurves.SelectedValue = l.Id;
                }

                CurveCanvas.Children.Add(l.LinePath);
                CurveCanvas.Children.Add(l.LineLabel);

            });
        }

        /// <summary>
        /// 附加一条Y轴曲线（用于并列试验）
        /// </summary>
        /// <param name="appendId">对应的Y轴Id并列试验编号</param>
        /// <param name="index"></param>
        /// <param name="id">自己的id对应多工位处理</param>
        public void AppendY(int appendId, int index, int? id)
        {
            Dispatcher.Invoke(() =>
            {
                var line = _lines.SingleOrDefault(l => l.Id == appendId);
                if (line != null)
                {
                    var l = line.Copy(_style, index, id);
                    l.OnPointAddHandler += L_OnPointAddHandler;
                    l.OnLineYAxisChangedHandler += L_OnLineYAxisChangedHandler;
                    CurveCanvas.Children.Add(l.LinePath);
                    CurveCanvas.Children.Add(l.LineLabel);
                    // SpButtons.Children.Add(l.LineLabel);
                    _curves.Add(new TextAndValue { Text = l.LineLabel.Text, Value = l.Id + "" });
                    _lines.Add(l);
                }
            });

        }
        /// <summary>
        /// 设置X轴信息
        /// </summary>
        /// <param name="data">数据描述</param>
        /// <param name="max">最大值</param>
        /// <param name="min">最小值</param>
        public void SetXAxis(FullData data, double max, double min)
        {
            Dispatcher.Invoke(() =>
            {
                _x = new Axis { Max = max, Min = min, OriginMax = max, OriginMin = min, Precision = data.Precision, Text = data.Text, Unit = data.Unit, Type = data.DataType == DataType.Int ? 1 : 0 };
                _xId = data.DataNo;
                TxtXLabel.Text = _x.Text;
                if (data.Unit != "null")
                    TxtXLabel.Text = TxtXLabel.Text + (string.IsNullOrEmpty(_x.Unit) ? "" : $"({_x.Unit})");
                _flashXScale();
            });
        }

        /// <summary>
        /// 添加一个数据点
        /// </summary>
        /// <param name="x"></param>
        /// <param name="y"></param>
        /// <param name="id"></param>
        public void AddPoint(double x, double y, int id)
        {
            Dispatcher.Invoke(() =>
            {
                var ischange = false;
                if (x > _x.Max)
                {

                    if (_x.Type == 1)
                    {
                        _x.Max = Math.Ceiling(_x.Max / 10) * 10;
                    }
                    else
                    {
                        _x.Max = x * 1.2;
                    }
                    ischange = true;
                }
                if (x < _x.Min)
                {

                    if (_x.Type == 1)
                    {
                        _x.Max = Math.Ceiling(_x.Max / 10) * 10;
                    }
                    else
                    {
                        _x.Min = x * 1.2;
                    }
                    ischange = true;
                }
                if (ischange)
                {
                    //刷新x轴坐标
                    _flashXScale();
                    //x轴极值发生改变，重新计算所有曲线的x坐标
                    foreach (var l in _lines)
                    {
                        l.FlashX(_x.Max, _x.Min);
                    }
                }
                //添加点
                var line = _lines.SingleOrDefault(l => l.Id == id);
                line?.Add(x, y);
            });
        }

        /// <summary>
        /// 添加一组数据点
        /// </summary>
        /// <param name="datas"></param>
        public void AddPoints(List<FullData> datas)
        {
            //查找X轴数据
            var fd = datas.SingleOrDefault(d => d.DataNo == _xId);
            if (fd == null) return;
            Dispatcher.Invoke(() =>
            {
                var x = fd.Value.ToDouble();
                var ischange = false;
                if (x > _x.Max)
                {
                    _x.Max = x * 1.2;
                    if (_x.Type == 1)
                    {
                        _x.Max = Math.Ceiling(_x.Max / 10) * 10;
                    }
                    ischange = true;
                }
                if (x < _x.Min)
                {
                    _x.Min = x * 1.2;
                    if (_x.Type == 1)
                    {
                        _x.Max = Math.Ceiling(_x.Max / 10) * 10;
                    }
                    ischange = true;
                }
                if (ischange)
                {
                    //刷新x轴坐标
                    _flashXScale();
                    //x轴极值发生改变，重新计算所有曲线的x坐标
                    foreach (var l in _lines)
                    {
                        l.FlashX(_x.Max, _x.Min);
                    }
                }

                //添加Y轴数据
                foreach (var line in _lines)
                {
                    fd = datas.SingleOrDefault(d => d.DataNo == line.Id);
                    if (fd != null)
                    {
                        var y = fd.Value.ToDouble();
                        line.Add(x, y);
                    }
                }
            });
        }

        /// <summary>
        /// 设置曲线标题
        /// </summary>
        /// <param name="title"></param>
        public void SetTitle(string title)
        {
            var font = new Font(TxtCurves.FontFamily.ToString(), (float)TxtCurves.FontSize, GraphicsUnit.Pixel);
            System.Drawing.Size proposedSize = new System.Drawing.Size(int.MaxValue, int.MaxValue);
            System.Windows.Forms.TextFormatFlags flags = System.Windows.Forms.TextFormatFlags.LeftAndRightPadding | System.Windows.Forms.TextFormatFlags.ExternalLeading;
            var sf = System.Windows.Forms.TextRenderer.MeasureText(title, font, proposedSize, flags);

            TxtCurves.Tag = title;
            TxtCurves.Width = sf.Width * 2.8;
        }

        /// <summary>
        /// 清空曲线（轴数据恢复初始值）
        /// </summary>
        public void Clear()
        {
            Dispatcher.Invoke(() =>
            {
                _style.Reset();
                //清空canvas
                CurveCanvas.Children.Clear();
                CurveCanvas.Children.Add(_axisPath);
                CurveCanvas.Children.Add(_bgPath);
                //重置X轴
                _x.Max = _x.OriginMax;
                _x.Min = _x.OriginMin;
                _flashXScale();
                //重置Y轴
                var rls = new List<int>();
                var index = 0;
                foreach (var line in _lines)
                {
                    if (line.IsDrawPoint)
                    {
                        foreach (var ell in line.LinePoints)
                        {
                            CurveCanvas.Children.Remove(ell);
                        }
                    }
                    if (line.IsMonopolizeY || line.Id > 0)
                    {
                        line.Clear();
                        line.MaxX = _x.Max;
                        line.MinX = _x.Min;
                        CurveCanvas.Children.Add(line.LinePath);
                    }
                    else
                    {
                        rls.Add(index);
                        CurveCanvas.Children.Remove(line.LinePath);
                        SpButtons.Children.Remove(line.LineLabel);
                    }
                    line.LineLabel.Text = "";
                    CurveCanvas.Children.Add(line.LineLabel);
                    index++;
                }

                foreach (var i in rls)
                {
                    _lines.RemoveAt(i);
                }
            });
        }

        /// <summary>
        /// 重置曲线
        /// </summary>
        public void Reset()
        {
            Dispatcher.Invoke(() =>
            {
                _style.Reset();
                CurveCanvas.Children.Clear();
                foreach (var line in _lines)
                {
                    line.Clear();
                    //line.OnLineLabelClickHandler -= L_OnLineLabelClickHandler;
                    line.OnPointAddHandler -= L_OnPointAddHandler;
                }

                foreach (var txt in GdXLabels.Children)
                {
                    if (txt is TextBlock tb) tb.Text = "";
                }

                GridYScale.Children.Clear();
                GridYScale.RowDefinitions.Clear();
                WpLegends.Children.Clear();
                SpButtons.Children.Clear();
                TxtXLabel.Text = "";
                TxtCurves.Tag = "";
                _lines.Clear();
                _curves.Clear();
                CurveCanvas.Children.Add(_axisPath);
                CurveCanvas.Children.Add(_bgPath);
                _isDrawBg = false;
            });
            GC.Collect();
        }

        public void ShowLastData()
        {
            foreach (var line in _lines)
            {
                Log.Debug().Message($"绘制{line.Y.Text}最后一点数值").Write();
                var pos = line.LineCore.Segments.LastOrDefault();

                if (pos != null)
                {
                    var data = line.LineDatas.LastOrDefault();
                    Log.Debug().Message($"{line.Y.Text}最后一点数值：{data.Y}").Write();
                    line.LineLabel.Text = (line.Y.Type == 2 ? data.Y.ScientificNotation(line.Y.Precision) : data.Y.Round(line.Y.Precision));
                    line.LineLabel.Visibility = line.IsShow ? Visibility.Visible : Visibility.Collapsed;
                    var ls = pos as LineSegment;
                    var p = ls.Point;
                    Log.Debug().Message($"{line.Y.Text}最后一的坐标：{p.X}，{p.Y}").Write();
                    var font = new Font(line.LineLabel.FontFamily.ToString(), (float)line.LineLabel.FontSize, GraphicsUnit.Pixel);
                    System.Drawing.Size proposedSize = new System.Drawing.Size(int.MaxValue, int.MaxValue);
                    System.Windows.Forms.TextFormatFlags flags = System.Windows.Forms.TextFormatFlags.LeftAndRightPadding | System.Windows.Forms.TextFormatFlags.ExternalLeading;
                    var sf = System.Windows.Forms.TextRenderer.MeasureText(line.LineLabel.Text, font, proposedSize, flags);
                    var t = p.Y - sf.Height / 2.0;
                    if (t >= CurveCanvas.Height)
                    {
                        t -= sf.Height / 2.0;
                    }
                    if (t <= 0)
                    {
                        t = 0;
                    }

                    if (p.X + sf.Width > CurveCanvas.Width)
                    {
                        Canvas.SetLeft(line.LineLabel, CurveCanvas.Width - sf.Width);
                        t -= sf.Height / 2.0;
                        if (t < 0) t = 0;
                        //Canvas.SetTop(line.LineLabel, p.Y);
                    }
                    else
                    {
                        Canvas.SetLeft(line.LineLabel, p.X);
                        //Canvas.SetTop(line.LineLabel, p.Y - line.LineLabel.Height / 2);
                    }
                    Canvas.SetTop(line.LineLabel, t);
                }
            }
        }

        private void _flash()
        {
            Dispatcher.Invoke(() =>
            {
                if ((int)CurveCanvas.Width == 0) return;
                if (_isDrawBg) return;
                _drawBgAndAxis();
                _initYAxis();
                _initBtnAndLegend();

                //foreach (var line in _lines)
                //{
                //    line.MinX = _x.Min;
                //    line.MaxX = _x.Max;
                //    line.ParentH = CurveCanvas.Height;
                //    line.ParentW = CurveCanvas.Width;
                //}
                _isDrawBg = true;
                UpdateLayout();
            });
        }

        public void Flash()
        {
            _flash();
        }

        private void L_OnPointAddHandler(object sender, PointEventArgs e)
        {
            CurveCanvas.Children.Add(e.Point);
        }

        //private void L_OnLineLabelClickHandler(object sender, EventArgs e)
        //{
        //    var line = sender as Line;
        //    if (line == null) return;
        //    if (line.IsShow)
        //    {
        //        line.Hide();
        //        return;
        //    }
        //    var c = _lines.Count(l => l.IsShow && l.IsMonopolizeY);
        //    if (line.IsMonopolizeY && c >= MaxYThreshold) return;
        //    line.Show();
        //}

        private void L_OnLineYAxisChangedHandler(object sender, EventArgs e)
        {
            var cl = sender as Line;
            if (cl == null) return;
            foreach (var line in _lines.Where(l => l.Id == -cl.Id))
            {
                line.FlashY(cl.Y.Max, cl.Y.Min);
            }
        }

        private void _flashXScale()
        {
            var xs = (_x.Max - _x.Min) / GridCount;
            for (int i = 0; i <= GridCount; i++)
            {
                var tb = GdXLabels.FindName("TxtX" + i) as TextBlock;
                if (tb != null)
                {
                    tb.Text = _round(_x.Min + i * xs, _x.Precision);
                }
            }
        }

        private string _round(double d, int digits)
        {
            string format = "0";
            if (digits > 0)
            {
                for (int j = 0; j < digits; j++)
                {
                    format += "0";
                }
                format = format.Insert(1, ".");
            }
            //return Math.Round(value, digits).ToString();
            return d.ToString(format);
        }
    }

    public class TextAndValue
    {
        public string Text { get; set; }

        public string Value { get; set; }
    }
}
